# 导入蓝图对象
import random

from info.models import User
from . import passport_blu
# 导入captcha扩展实现图片验证码的生成
from info.utils.captcha.captcha import captcha
# 导入flask内置的对象
from flask import request, jsonify, current_app, make_response,session
# 导入自定义的状态码
from info.utils.response_code import RET
# 导入redis实例,# 导入常量配置文件
from info import redis_store,constants, db
# 导入正则模块
import re
# 导入云通讯
from info.libs.yuntongxun import sms


# 发送图片验证码
@passport_blu.route('/image_code')
def generate_image_code():
    """
    生成图片验证码：
    1. 从浏览器获取UUID
    2. 引用captcha生成图片验证码，name,image,text
    3. 在redis数据库中保存text，利用ImageCode_UUID拼接作为内容的名子
    4. 使用make_response返回图片验证码给前段
    5. 修改返回的head,Content-Type = image/jpg
    :return:
    """
    # 获取UUID
    image_code_id = request.args.get('image_code_id')
    print(image_code_id)
    # 校验参数存在性
    if not image_code_id:
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')
    # 生成图片验证码
    name, text, image = captcha.generate_captcha()
    # 存储图片验证码信息
    try:
        redis_store.setex('ImageCode_'+image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
    except Exception as e:
        # 使用应用上下问current_app在日志中保存错误信息
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='保存图片验证码信息失败')
    # 如果没有异常，返回图片验证码本身
    else:
        response = make_response(image)
        response.headers['Content-Type'] = 'image/jpg'
        return response


# 发送短信验证码
@passport_blu.route('/sms_code', methods=['post'])
def send_sms_code():
    """
    1. 接收参数mobile, image_code, image_code_id
    2. 校验参数完整性，如果参数不完整，直接结束程序运行
    3. 校验mobile的格式，使用正则
    4. 校验验证码
    5. 从redis数据库中获取真是的图片验证码
    6. 进行验证码比对
    7. 验证码只能校验一次的本质是：为了防止非人类注册者多次校验， 无论校验是否成功，都要从redis数据库中删除
    8. 生成6位随机数验证码
    9. 判断手机号是否已经注册
    10. 将6位随机验证码保存到redis数据库中
    11. 通过第三方云通讯发送验证码到注册用户手机
    12. 判断发送是否成功，如果返回0则成功，否则失败
    13. 返回结果

    :return:
    """
    # 接收参数
    mobile = request.json.get('mobile')
    image_code = request.json.get('image_code')
    image_code_id = request.json.get('image_code_id')
    # print(mobile)
    # print(image_code)
    # print(image_code_id)
    # 判断参数完整性
    if not all([mobile,image_code,image_code_id]):
        return jsonify(errno=RET.PARAMERR, errmsg='传入参数不完整')
    # 进一步校验参数数据，手机号，使用正则
    if not re.match(r'1[3456789]\d{9}',mobile):
        return jsonify(errno=RET.DATAERR, errmsg='手机号格式错误')
    # 从数据库中获取真是验证码
    try:
        real_image_code = redis_store.get('ImageCode_' + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='验证码不存在')
    # 检查获取结果
    if not real_image_code:
        return jsonify(errrno=RET.DATAERR, errmsg='验证码不存在')
    # 删除图片验证码
    try:
        redis_store.delete('ImageCode_' + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
    # 比较验证码
    if real_image_code.lower() != image_code.lower():
        return jsonify(errno=RET.DATAERR, errmsg='验证码输入错误')
    # 检查手机号是否已经注册
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='查询用户数据失败')
    else:
        # 判断手机号是否已经注册
        if user is not None:
            return jsonify(errno=RET.DATAERR, errmsg='号码已注册')
    # 生成短信随机数
    sms_code = '%06d' % random.randint(0,999999)
    # 把生成的随机熟保存到redis中
    try:
        redis_store.setex('SMSCode_'+mobile,constants.SMS_CODE_REDIS_EXPIRES,sms_code)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据保存失败')
    # 第三方发送验证码
    try:
        # 实例化对象
        cpp = sms.CCP()
        result = cpp.send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES],1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg='短信发送失败')
    # 判断发送结果
    if result == 0:
        return jsonify(errno=RET.OK, errmsg='发送成功')
    else:
        return jsonify(errno=RET.THIRDERR, errmsg='发送失败')


# 用户注册
@passport_blu.route('/register', methods=['POST'])
def register():
    """
    注册用户
    1、获取参数：mobile,sms_code,password
    2、校验参数的完整性
    3、校验手机号的格式
    4、获取本地存储的真实短信验证码
    5、校验获取结果
    6、先比较短信是否一致，如果不一致直接终止程序运行
    7、再删除redis中的短信验证码
    8、构造模型类的实例对象
    9、user = User();
    # 对密码进行加密存储
    user.password = password
    10、提交用户数据到mysql数据库中；
    11、提交数据成功后，把用户的基本信息mobile,id,nick_name，缓存到redis中；
    12、返回结果
    :return:
    """
    # 获取参数
    mobile = request.json.get('mobile')
    sms_code = request.json.get('sms_code')
    password = request.json.get('password')
    # 校验参数完整性
    if not all([mobile,sms_code,password]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数不完整')
    # 校验手机号格式
    if not re.match(r'1[3456789]\d{9}',mobile):
        return jsonify(errno=RET.DATAERR, errmsg='手机号格式错误')
    # 判断手机号是否已经注册
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询数据库失败')
    else:
        if user is not None:
            return jsonify(errno=RET.IOERR, errmsg='手机号已注册')
    # 获取本地真是的短信验证码
    try:
        real_sms_code = redis_store.get('SMSCode_'+mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='验证码不存在')
    # 判断是否获得真实验证码
    if not real_sms_code:
        return jsonify(errno=RET.DATAERR, errmsg='验证码不存在')
    # 校验短信验证码
    if sms_code != real_sms_code:
        return jsonify(errno=RET.IOERR, errmsg='验证码输入错误')
    # 如果短信验证码校验成功，则删除redis中的短信验证码
    try:
        redis_store.delete('SMSCode_'+mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='验证码删除失败')
    # 构造模型类
    user = User()
    user.mobile = mobile
    user.nick_name = mobile
    # 实际上调用了模型类中的方法，对密码进行了sha256加密存储
    user.password = password
    # 将user保存到mysql数据库
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 发生异常，进行回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg='数据库操作失败,数据为存入')
    # 利用flask中内置的session将数据存储到redis中
    session['user_id'] = user.id
    session['nick_name'] = mobile
    session['mobile'] = user.mobile
    # 返回结果
    return jsonify(errno=RET.OK,errmsg='注册成功')


# 用户登录接口
@passport_blu.route('/login', methods=['POST'])
def login():
    """
    1. 获取参数mobile, password
    2. 验证参数完整性
    3. 判断mobile是否符合手机号格式
    4. 判断手机号是否存在数据库中
    5. 获取真实密码
    6. 同时判断手机号和密码是否正确
    7. 缓存用户信息到redis
    8. 返回登陆成功


    :return:
    """
    # 获取参数
    mobile = request.json.get('mobile')
    password = request.json.get('password')
    # 判断参数完整性
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数不完整')
    # 判断手机格式
    if not re.match(r'1[3456789]\d{9}',mobile):
        return jsonify(errno=RET.IOERR, errmsg='手机号格式错误')
    # 根据手机号查询mysql数据库，确认用户是否存在
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='用户不存在')
    # 判断手机号和密码
    # 如果用户不存在或者密码检查不正确，则登陆失败
    if user is None or not user.check_password(password):
        return jsonify(errno=RET.LOGINERR, errmsg='登录失败，用户名或密码错误')
    # 缓存登陆信息
    session['user_id'] = user.id
    # 用户昵称不能缓存手机号，因为用户有可能修改过了用户昵称
    session['nick_name'] = user.nick_name
    session['mobile'] = mobile
    # 返回结果
    return jsonify(errno=RET.OK, errmsg='登陆成功')

# 用户退出
@passport_blu.route('/logout')
def logout():
    """
    用户退出
    RESTful风格，如果对服务器的资源是什么操作，就使用对应的请求方法；比如退出的请求方法为delete
    url中不允许出现动词，url代表的是资源，资源使用名词，一般会映射数据库中表；
    $.ajax({
        url：路径，
        type:'delete'
    })
    退出的本质是清除用户的缓存数据，清楚数据库中的真实数据，不建议使用get请求，post,delete
    :return:
    """
    # 清除在redis数据库中的缓存信息
    # session.clear()会把用户的所有信息全部清空
    session.pop('user_id')
    session.pop('nick_name')
    session.pop('mobile')
    return jsonify(errno=RET.OK,errmsg='OK')